package jp.syuriken.snsw.lancher;

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Level;

import jp.syuriken.snsw.terenaibot.BotSystem;
import jp.syuriken.snsw.terenaibot.ConfigurationException;
import jp.syuriken.snsw.utils.Logger;
import jp.syuriken.snsw.utils.SimpleLoggerFormatter;
import jp.syuriken.snsw.utils.SimpleLoggerFormatter2;

/**
 * TerenaiBotSystemを起動するためのランチャー。
 * 
 * @author $Author: snswinhaiku.lo@gmail.com $
 */
public class TerenaiBotSystemLancher {
	
	/** デフォルトログファイル */
	private static final String DEFAULT_LOG_FILENAME = "tbotsys.log";
	
	/** デフォルト設定ファイル */
	private static final String DEFAULT_CONFIG_FILENAME = "tbotsys.cfg";
	
	private static boolean isFinish = false;
	
	private static Logger logger;
	
	private static BotSystem bt;
	
	private static LongOpt[] longOpts = new LongOpt[] {
		new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'v'),
		new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
		new LongOpt("log-level", LongOpt.REQUIRED_ARGUMENT, null, 'L'),
		new LongOpt("debug", LongOpt.NO_ARGUMENT, null, 'd'),
		new LongOpt("config-file", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
		new LongOpt("log-file", LongOpt.REQUIRED_ARGUMENT, null, 'l')
	};
	

	private static String getStringFromResource(ResourceBundle bundle, String key, String defaultValue) {
		try {
			return bundle.getString(key);
		} catch (MissingResourceException e) {
			return defaultValue;
		}
	}
	
	/**
	 * ヘルプを表示する。
	 * @param versionBundle バージョンを格納したプロパティファイル 
	 * 
	 */
	private static void help(ResourceBundle versionBundle) {
		System.out.println("オプション一覧：");
		System.out.println("  -L  --log-level [LEVEL]  ログに出力するレベルを設定します");
		System.out.println("  -v  --version            バージョンを表示");
		System.out.println("  -h  --help               このヘルプを表示");
		System.out.println("  -d  --debug              デバッグします (例外時に多くの情報が得られます)");
		System.out.println("  -c  --config-file [FILE] 設定ファイルを指定する [default=tbotsys.cfg]");
		System.out.println("  -l  --log-file [FILE]    ログファイルを指定する");
	}
	
	/**
	 * バージョンを表示する。
	 * @param versionBundle バージョンを格納したプロパティファイル 
	 * 
	 */
	private static void helpVersion(ResourceBundle versionBundle) {
		String artifactName = getStringFromResource(versionBundle, "pom.name", "<unknown>");
		String artifactId = getStringFromResource(versionBundle, "pom.artifactId", "<unknown>");
		String version = getStringFromResource(versionBundle, "pom.version", "<unknown>");
		if (version.endsWith("-SNAPSHOT")) {
			version +=
					" (r" + getStringFromResource(versionBundle, "svn.committedRevision", "<unknown revision>") + ")";
		}
		String supportUrl = getStringFromResource(versionBundle, "pom.url", "<undefined>");
		
		System.out.println(artifactName + " (" + artifactId + " " + version + ")");
		System.out.println("Support URL: " + supportUrl);
		System.out.println();
		System.out.println("Java version: " + System.getProperty("java.version", "<unknown java version>"));
		System.out.println("Java home: " + System.getProperty("java.home", "<unknown java home>"));
		System.out.println("Default locale: " + Locale.getDefault() + ", platform encoding: "
				+ System.getProperty("file.encoding", "<unknown encoding>"));
	}
	
	/**
	* 起動。
	* 
	* @param args オプションを格納した配列。
	*/
	public static void main(String[] args) {
		ResourceBundle versionBundle = ResourceBundle.getBundle("jp.syuriken.snsw.terenaibot.version");
		Getopt options = new Getopt(versionBundle.getString("pom.artifactId"), args, "l:c:L:vhd", longOpts);
		int c;
		boolean printVersion = false;
		boolean printHelp = false;
		boolean isDebug = false;
		Level level = Level.FINE;
		String configFile = DEFAULT_CONFIG_FILENAME;
		String logFile = DEFAULT_LOG_FILENAME;
		while ((c = options.getopt()) != -1) {
			switch (c) {
				case 'L':
					try {
						Level.parse(options.getOptarg());
					} catch (IllegalArgumentException e) {
						System.err.println(e.getLocalizedMessage());
					}
					break;
				case 'v':
					printVersion = true;
					break;
				case 'h':
					printHelp = true;
					break;
				case 'd':
					isDebug = true;
					break;
				case 'c':
					configFile = options.getOptarg();
					break;
				case 'l':
					logFile = options.getOptarg();
					break;
				default:
					System.err.println("引数指定の誤り：未知の引数が指定");
			}
		}
		if (printHelp) {
			help(versionBundle);
			return;
		} else if (printVersion) {
			helpVersion(versionBundle);
			return;
		}
		try {
			SimpleLoggerFormatter loggerFormatter = new SimpleLoggerFormatter2();
			loggerFormatter.setDebug(isDebug);
			logger = Logger.getLogger(logFile, level, loggerFormatter);
			logger.addWriter(new PrintWriter(System.out));
		} catch (IOException e) {
			System.err.println(e.getLocalizedMessage());
			System.exit(1);
		}
		try {
			bt = new BotSystem(logger, new File(configFile));
		} catch (FileNotFoundException e) {
			logger.log(Level.SEVERE, "設定ファイル" + configFile + "が見つかりません");
			System.exit(1);
		} catch (IOException e) {
			logger.log(Level.SEVERE, e);
			System.exit(1);
		} catch (ConfigurationException e) {
			logger.log(Level.SEVERE, e);
			System.exit(1);
		}
		bt.start();
		Runtime.getRuntime().addShutdownHook(new Thread() {
			
			@Override
			public void run() {
				isFinish = true;
				bt.stopBot();
				try {
					bt.join();
				} catch (InterruptedException e) {
					// do nothing
				}
				try {
					logger.flush();
				} catch (IOException e) {
					System.err.println(e.getLocalizedMessage());
				}
				try {
					logger.close();
				} catch (IOException e) {
					System.err.println(e.getLocalizedMessage());
				}
			}
		});
		
		do {
			try {
				bt.join();
			} catch (InterruptedException e) {
				// do nothing
			}
		} while (!isFinish);
	}
	
	private TerenaiBotSystemLancher() {
	}
}
